﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using StudentEFConsoleApp.EntityFramework.ExtensionMethods;

using System.Data.Entity;
using SchoolPOCOProxyDAL;
using SchoolPOCOProxyDAL.POCOProxyEntities;

namespace StudentEFConsoleApp
{
    public class POCOProxyInDisconnectedScenario
    {
        public static void AddDemo()
        {
            Student student = new Student();
            student.StudentName = "POCOProxyInDisconnectedScenario student1";

            Standard std = new Standard();
            std.StandardName = "POCOProxyInDisconnectedScenario Standard1";

            student.Standard = std;

            using (SchoolDBContext ctx = new SchoolDBContext())
            {
                ctx.Students.AddObject(student);
                ctx.SaveChanges();

                Console.WriteLine("POCOProxyInDisconnectedScenario AddDemo successfull");
            }
        }

        public static void AddOneToOneDemo()
        {

            Student student = new Student();
            student.StudentName = "POCOProxyInDisconnectedScenario student2";

            Standard std = new Standard();
            std.StandardName = "POCOProxyInDisconnectedScenario Standard2";

            student.Standard = std;

            StudentAddress sAddress = new StudentAddress();

            sAddress.Address1 = "POCOProxyInDisconnectedScenario Address1";
            sAddress.Address2 = "POCOProxyInDisconnectedScenario Address2";
            sAddress.City = "POCOProxyInDisconnectedScenario City1";
            sAddress.State = "POCOProxyInDisconnectedScenario State1";

            student.StudentAddress = sAddress;

            using (SchoolDBContext ctx = new SchoolDBContext())
            {

                ctx.Students.AddObject(student);
                ctx.SaveChanges();

                Console.WriteLine("POCOProxyInDisconnectedScenario AddOneToOneDemo Successfull");
            }
        }

        //Adding many teachers for one standard
        public static void AddOneToManyDemo()
        {

            Standard std = new Standard();

            std.StandardName = "POCOProxy disconnected standard";
            std.Description = "Demo standard";

            Teacher teacher1 = new Teacher();
            teacher1.TeacherName = "POCOProxy disconnected Teacher1";

            Teacher teacher2 = new Teacher();
            teacher2.TeacherName = "POCOProxy disconnected Teacher2";

            Teacher teacher3 = new Teacher();
            teacher3.TeacherName = "POCOProxy disconnected Teacher3";


            //Adding many teachers for one standard
            std.Teachers.Add(teacher1);
            std.Teachers.Add(teacher2);
            std.Teachers.Add(teacher3);

            using (SchoolDBContext ctx = new SchoolDBContext())
            {
                ctx.Standards.AddObject(std);
                ctx.SaveChanges();

                Console.WriteLine("POCOProxyInDisconnectedScenario AddOneToManyDemo Successfull");
            }
        }

        //Add new student and new courses for the student as student and course has many-to-many relation.
        public static void AddManyToManyDemo()
        {
            Student student = new Student();
            student.StudentName = "POCOProxyInDisconnectedScenario student3";

              Standard std = new Standard();
                std.StandardName = "POCOProxyInDisconnectedScenario standard4";
                std.Description = "Demo standard";

            student.Standard = std;

            Course course1 = new Course();
            course1.CourseName = "Course1 POCOProxyInDisconnectedScenario student2";
            course1.Location = "City1";

            Course course2 = new Course();
            course2.CourseName = "Course2 POCOProxyInDisconnectedScenario student2";
            course2.Location = "City2";

            Course course3 = new Course();
            course3.CourseName = "Course3 POCOProxyInDisconnectedScenario student2";
            course3.Location = "City1";

            Teacher teacher1 = new Teacher();
            teacher1.TeacherName = "POCOProxyInDisconnectedScenario teacher1";
            teacher1.Standard = std;

            //assign teacher1 for each courses
            course1.Teacher = teacher1;
            course2.Teacher = teacher1;
            course3.Teacher = teacher1;

            //Add courses to student
            student.Courses.Add(course1);
            student.Courses.Add(course2);
            student.Courses.Add(course3);


            using (SchoolDBContext ctx = new SchoolDBContext())
            {
                //add whole student entity graph to context
                ctx.Students.AddObject(student);
                ctx.SaveChanges();
            }

            Console.WriteLine("POCOProxyInDisconnectedScenario AddManyToManyDemo Successfull");

        }


        public static void UpdateDemo()
        {
            Student stud = null;
            using (SchoolDBContext ctx = new SchoolDBContext())
            {
                ctx.ContextOptions.LazyLoadingEnabled = false;
                
                stud = (from s in ctx.Students
                        where s.StudentName == "POCOProxyInDisconnectedScenario student1"
                        select s).FirstOrDefault();

            }

            stud.StudentName = "Updated POCOProxyInDisconnectedScenario student1";

            using (SchoolDBContext newCtx = new SchoolDBContext())
            {
                newCtx.Students.Attach(stud);
                newCtx.ObjectStateManager.ChangeObjectState(stud, System.Data.EntityState.Modified);
                newCtx.SaveChanges();
            }

            Console.WriteLine("POCOProxyInDisconnectedScenario UpdateDemo successfull");

        }

        public static void UpdateOneToOneDemo()
        {
            Student student = null;

            using (SchoolDBContext ctx = new SchoolDBContext())
            {
                ctx.ContextOptions.LazyLoadingEnabled = false;

                student = (from s in ctx.Students.Include("StudentAddress")
                                   where s.StudentName == "POCOProxyInDisconnectedScenario student2"
                                   select s).FirstOrDefault<Student>();


            }

            student.StudentName = "Updated POCOProxyInDisconnectedScenario student2"; 

            //update student address
            student.StudentAddress.Address1 = "Updated POCOProxyInDisconnectedScenario Address1";
            student.StudentAddress.Address2 = "Updated POCOProxyInDisconnectedScenario Address2";
            student.StudentAddress.City = "Updated POCOProxyInDisconnectedScenario City";
            student.StudentAddress.State = "Updated POCOProxyInDisconnectedScenario State";

            //student.StudentAddress = sAddress;

            using (SchoolDBContext newCtx = new SchoolDBContext())
            {
                newCtx.Students.Attach(student);
                newCtx.ObjectStateManager.ChangeObjectState(student, System.Data.EntityState.Modified);
                newCtx.ObjectStateManager.ChangeObjectState(student.StudentAddress, System.Data.EntityState.Modified);
                newCtx.SaveChanges();
            }

            Console.WriteLine("POCOProxyInDisconnectedScenario UpdateOneToOneDemo Successfull");

        }


        public static void UpdateOneToManyDemo()
        {
            Standard std = null;

            using (SchoolDBContext ctx = new SchoolDBContext())
            {
                ctx.ContextOptions.LazyLoadingEnabled = false;
                //fetching existing standard from the db
                std = (from s in ctx.Standards.Include("Teachers")
                       where s.StandardName == "POCOProxy disconnected standard"
                       select s).FirstOrDefault<Standard>();

            }

            std.StandardName = "Updated POCOProxy disconnected standard";
            std.Description = "Updated standard";

            if (std.Teachers  != null)
            {
                if (std.Teachers.Count >= 2)
                {
                    //get the first element to be updated
                    Teacher updateTchr = std.Teachers.ElementAt<Teacher>(0);

                    //get the second element to be removed
                    Teacher deletedTchr = std.Teachers.ElementAt<Teacher>(1);

                    //remove updated teacher to re-add later
                    std.Teachers.Remove(updateTchr);

                    //delete second teacher from the list
                    // deleted second teacher
                    std.Teachers.Remove(deletedTchr);

                    //Update first teacher in the list
                    updateTchr.TeacherName = "Updated POCOInDisconnectedScenario Teacher1";

                    // re-add first teacher
                    std.Teachers.Add(updateTchr); 
                }
            }

            // adding new teacher for selected standard
            Teacher newTeacher = new Teacher();
            newTeacher.TeacherName = "New POCOProxy disconnected Teacher";
            std.Teachers.Add(newTeacher);

            
            //Save standard and tearchers Updated above in different context just to create disconnected scenario
            using (SchoolDBContext newCtx = new SchoolDBContext())
            {
                //fetch existing standard info
                var exitingStandard = (from s in newCtx.Standards
                                       where s.StandardId == std.StandardId
                                       select s).FirstOrDefault<Standard>();


                var newTeachers = std.Teachers.ToList<Teacher>();
                var existingTeachers = exitingStandard.Teachers.ToList<Teacher>();

                // find added teachers from newTeachers whose TeacherId doesn't match with Existing teachers
                var addedTeachers = newTeachers.Except(existingTeachers, tchr => tchr.TeacherId);

                // find deleted teachers from existing (from db) teachers whose TeacherId is not in newTeachers list
                var deletedTeachers = exitingStandard.Teachers.Except(newTeachers, tchr => tchr.TeacherId);

                //find Updated teachers from existing teachers which is either not deleted or Added
                var updatedTeachers = exitingStandard.Teachers.Except(deletedTeachers, tchr => tchr.TeacherId);

                //Add new teachers to context
                addedTeachers.ToList<Teacher>().ForEach(t => newCtx.Teachers.AddObject(t));

                //Attacher Updated teachers to context and mark it's state as Modified
                foreach (Teacher t in updatedTeachers)
                {
                    newCtx.Teachers.Attach(t);
                    newCtx.ObjectStateManager.ChangeObjectState(t, System.Data.EntityState.Modified);
                }

                // delete the deleted teachers in the context
                deletedTeachers.ToList<Teacher>().ForEach(t => newCtx.Teachers.DeleteObject(t));

                // save all above activity
                newCtx.SaveChanges();

                Console.WriteLine("POCOProxyInDisconnectedScenario UpdateOneToManyDemo Successfull");
            }
        }

       //removes one course from student's course lists
        public static void UpdateManyToManyDemo()
        {
            Student student = null;

            using (SchoolDBContext ctx = new SchoolDBContext())
            {
                ctx.ContextOptions.LazyLoadingEnabled = false;

                student = (from s in ctx.Students.Include("Courses")
                                   where s.StudentName == "POCOProxyInDisconnectedScenario student3"
                                   select s).FirstOrDefault<Student>();

            }

            student.StudentName = "Updated POCOProxyInDisconnectedScenario student3";

            //You can add existing courses to courseList which is not assigned to above student. I skip that part.

            Course cours = student.Courses.FirstOrDefault<Course>();
            //removing first course from student's existing courses
            student.Courses.Remove(cours);

            using (SchoolDBContext newCtx = new SchoolDBContext())
            {
                newCtx.ContextOptions.LazyLoadingEnabled = false;
                
                //fetch existing student
                var dbStudent = (from s in newCtx.Students.Include("Courses")
                                       where s.StudentID == student.StudentID
                                       select s).FirstOrDefault<Student>();

                var newCourses = student.Courses.ToList<Course>();
                var dbCourses = dbStudent.Courses.ToList<Course>();

                //You may skip this if you update only courses of the student but not student itself.
                newCtx.ApplyCurrentValues<Student>("Students", student);

                //new course or exiting courses added to student's courses
                var addedCourses = newCourses.Except(dbCourses, cs => cs.CourseId).ToList<Course>();

                var deletedCourses = dbCourses.Except(newCourses, cs => cs.CourseId).ToList<Course>();

                addedCourses.ForEach(cs => dbStudent.Courses.Add(cs));
                deletedCourses.ForEach(cs => dbStudent.Courses.Remove(cs));

                newCtx.SaveChanges();

            }

            Console.WriteLine("POCOProxyInDisconnectedScenario UpdateManyToManyDemo Successfull");

        }

       
        public static void DeleteDemo()
        {
            Student stud = null;

            using (SchoolDBContext ctx = new SchoolDBContext())
            {
                ctx.ContextOptions.LazyLoadingEnabled = false;

                stud = (from s in ctx.Students
                        where s.StudentName == "Updated POCOProxyInDisconnectedScenario student1"
                            select s).FirstOrDefault();
            }

            using (SchoolDBContext newCtx = new SchoolDBContext())
            {
                newCtx.Students.Attach(stud);
                newCtx.Students.DeleteObject(stud);
                //you can use ObjectStateManager also
                //ctx2.ObjectStateManager.ChangeObjectState(stud, System.Data.EntityState.Deleted);
                int num = newCtx.SaveChanges();
            }
            Console.WriteLine("POCOProxyInDisconnectedScenario DeleteDemoInDisconnectedScenario successfull");
        }

        public static void DeleteOneToOneDemo()
        {
            Student student = null;

            using (SchoolDBContext ctx = new SchoolDBContext())
            {
                student = (from s in ctx.Students.Include("StudentAddress")
                           where s.StudentName == "Updated POCOProxyInDisconnectedScenario student2"
                                   select s).FirstOrDefault<Student>();

            }

 
            using (SchoolDBContext newCtx = new SchoolDBContext())
            {
                newCtx.StudentAddresses.Attach(student.StudentAddress);
                newCtx.StudentAddresses.DeleteObject(student.StudentAddress);

                newCtx.SaveChanges();

                Console.WriteLine("POCOProxyInDisconnectedScenario DeleteOneToOneDemo Successfull");
            }
        }

        public static void DeleteOneToManyDemo()
        {
            Standard std = null;

            using (SchoolDBContext ctx = new SchoolDBContext())
            {
                ctx.ContextOptions.LazyLoadingEnabled = false;

                //fetching existing standard from the db
                std = (from s in ctx.Standards.Include("Teachers")
                                where s.StandardName == "Updated POCOProxy disconnected standard"
                                select s).FirstOrDefault<Standard>();

            }

            using (SchoolDBContext newCtx = new SchoolDBContext())
            {
                
                //getting first teacher to be removed
                Teacher tchr = std.Teachers.FirstOrDefault<Teacher>();

                newCtx.Teachers.Attach(tchr);
                //removing teachers 
                newCtx.Teachers.DeleteObject(tchr);

                //Do not use std.Teachers.Remove(tchr). It will give exception. 
                //This statement doesn't remove teacher from teachers collection but it trying to remove the relationship.
                //std.Teachers.Remove(tchr);

                newCtx.SaveChanges();

            }
            Console.WriteLine("POCOProxyInDisconnectedScenario DeleteOneToManyDemo Successfull");
        }

        //removes one course from student's course lists: same as update
        public static void DeleteManyToManyDemo()
        {
            Student student = null;

            using (SchoolDBContext ctx = new SchoolDBContext())
            {
                ctx.ContextOptions.LazyLoadingEnabled = false;

                student = (from s in ctx.Students.Include("Courses")
                           where s.StudentName == "Updated POCOProxyInDisconnectedScenario student3"
                                   select s).FirstOrDefault<Student>();

            }

            Course cours = student.Courses.FirstOrDefault<Course>();

            //removing course from student
            student.Courses.Remove(cours);

            using (SchoolDBContext newCtx = new SchoolDBContext())
            {

               var dbStudent = (from s in newCtx.Students.Include("Courses")
                           where s.StudentID == student.StudentID 
                           select s).FirstOrDefault<Student>();

               var deletedCourses = dbStudent.Courses.Except(student.Courses, cs => cs.CourseId).ToList<Course>();

               deletedCourses.ForEach(cs => dbStudent.Courses.Remove(cs));

                newCtx.SaveChanges();
            }

            Console.WriteLine("POCOProxyInDisconnectedScenario DeleteManyToManyDemo Successfull");
        }
    }
}
